﻿using System.Collections.Generic;
using UnityEngine;
using Yoozoo.Core.Extensions.CSharp;
using Yoozoo.Core.Extensions.Engine;
using Yoozoo.Managers.ResourceManagerV2.Runtime;
using Object = UnityEngine.Object;

public class CharacterSystemCtrl : MonoBehaviour
{

    [Header("记录BlendShape节点")] public GameObject blendshapeObject;

    [Header("肤色影响节点")] public List<GameObject> skinColorObjects;
    
    [Header("肤色影响字段")] public string skinColorMaterialName;

    [Header("脸部控制节点")] public GameObject faceCtrlObject;
    
    [Header("穿衣状态")] public List<GameObject> stateClothingObjects;

    [Header("赤裸状态")] public List<GameObject> stateNakedObjects;

    private ResLoader m_resLoader;
    
    //合入后的节点
    private GameObject hairObj;
    private string hairMeshObjName;
    private string hairColorMaterialName = "_BaseColor";

    void OnDestroy()
    {
        m_resLoader?.Recycle2Cache();
        m_resLoader = null;
    }
    
    #region 换装
    
    /// <summary>
    /// 切换是否穿衣状态
    /// </summary>
    /// <param name="isNaked"></param>
    public void ChangeClothingState(bool isNaked)
    {
        Debug.Log(string.Format("[C#][Character] ChangeClothingState = {0}", isNaked));
        foreach (GameObject obj in stateClothingObjects) obj.SetActive(!isNaked);
        foreach (GameObject obj in stateNakedObjects) obj.SetActive(isNaked);
    }

    public void ChangeHairHtml(string assetPath, string colorHtml)
    {
        ColorUtility.TryParseHtmlString(colorHtml, out Color color);
        ChangeHair(assetPath, color);
    }

    /// <summary>
    /// 换发型
    /// </summary>
    /// <param name="assetPath"></param>
    public void ChangeHair(string assetPath, Color color)
    {
        if (!hairMeshObjName.IsEmpty())
        {
            var obj = gameObject.transform.Find(hairMeshObjName);
            obj.gameObject.Destroy();
            hairMeshObjName = string.Empty;
        }

        //在加载新的发型
        if (m_resLoader == null)
            m_resLoader = ResLoader.Alloc();

        m_resLoader.Add2Load(assetPath, (success, assetName, asset) =>
        {
            if (success && asset is GameObject target)
            {
                CombineAndChangeColor(target, color);
            }
        });
    }

    public void CombineAndChangeColor(GameObject target, Color color)
    {
        GameObject obj = GameObject.Instantiate(target);
                
        GameObject[] prefabs = new GameObject[2];
        prefabs[0] = gameObject;
        prefabs[1] = obj;

        MeshCombine(prefabs, out hairMeshObjName);
        obj.Destroy();

        if (!hairMeshObjName.IsEmpty())
        {
            hairObj = gameObject.transform.Find(hairMeshObjName).gameObject;
            if (hairObj != null && color != null)
                ChangeHairColor(color);
            else
                Debug.Log("Character CombineAndChangeColor failed! hairObj is null");
        }
        else
        {
            Debug.Log("Character CombineAndChangeColor failed! hairMeshObjName is Empty");
        }
    }

    #endregion
    
    #region 换色

    /// <summary>
    /// 更换皮肤颜色
    /// </summary>
    /// <param name="color"></param>
    public void ChangeSkinColor(Color color)
    {
        foreach (GameObject obj in skinColorObjects)
            SetMaterialColor(obj, color, skinColorMaterialName);
    }

    public void ChangeHairColorByHtml(string colorHtml)
    {
        ColorUtility.TryParseHtmlString(colorHtml, out Color color);
        ChangeHairColor(color);
    }
    
    /// <summary>
    /// 更换发型颜色
    /// </summary>
    public void ChangeHairColor(Color color)
    {
        if (!hairMeshObjName.IsEmpty())
        {
            GameObject obj = gameObject.transform.Find(hairMeshObjName).gameObject;
            Debug.Log(string.Format(
                "[C#] Character : ChangeHairColor = {0}, {1}, {2}, {3}", 
                color.r, color.g, color.b, color.a));
            SetMaterialColor(obj, color, hairColorMaterialName);
        }
    }

    private void SetMaterialColor(GameObject target, Color color, string key)
    {
        if (target != null)
        {
            var skinnedMesh = target.transform.GetComponent<SkinnedMeshRenderer>();
            if (skinnedMesh != null)
            {
                foreach (Material mat in skinnedMesh.materials)
                    mat.SetColor(key, color);
            }
            else
            {
                var mesh = target.transform.GetComponent<MeshRenderer>();
                if (mesh != null) {
                    foreach (Material mat in mesh.materials)
                        mat.SetColor(key, color);
                }
                else
                {
                    Debug.Log("Character SetMaterialColor failed! mesh is null");
                }
            }
        }
        else
        {
            Debug.Log("Character SetMaterialColor failed! target is null");
        }
    }
    
    #endregion
    
    #region 网格合并
    
    private void MeshCombine(GameObject[] prefabs, out string targetName){
        var mainPrefab = prefabs[0];
        var mainBoneRoot = mainPrefab.transform.Find("Root");
        targetName = string.Empty;

        for (var i = 1; i < prefabs.Length; i++)
        {
            var prefab = prefabs[i];
            MergeNodes(prefab.transform.Find("Root"), mainBoneRoot);
            var skinnedMeshRenders = prefab.GetComponentsInChildren<SkinnedMeshRenderer>();
            
            foreach (var skinnedMeshRenderer in skinnedMeshRenders)
            {
                string rootBonePath = GetNodePath(skinnedMeshRenderer.rootBone);
                var bonePaths = new List<string>();
                foreach (var bone in skinnedMeshRenderer.bones)
                {
                    bonePaths.Add(GetNodePath(bone));
                }

                var newMeshObject = Object.Instantiate(skinnedMeshRenderer.gameObject, mainPrefab.transform);
                newMeshObject.name = skinnedMeshRenderer.gameObject.name;
                var newSkinnedMeshRenderer = newMeshObject.GetComponent<SkinnedMeshRenderer>();
                newSkinnedMeshRenderer.rootBone = FindNode(mainBoneRoot, rootBonePath);
                var bones = new Transform[bonePaths.Count];
                for (var index = 0; index < bonePaths.Count; index++)
                {
                    var bonePath = bonePaths[index];
                    bones[index] = FindNode(mainBoneRoot, bonePath);
                }

                newSkinnedMeshRenderer.bones = bones;
                targetName = newMeshObject.name;
            }
        }
        
    }

    /// <summary>
    /// 将formRoot的子节点合并到toRoot中
    /// </summary>
    /// <param name="toRoot"></param>
    /// <param name="fromRoot"></param>
    private static void MergeNodes(Transform fromRoot, Transform toRoot)
    {
        int fromChildCount = fromRoot.childCount;
        for (int i = 0; i < fromChildCount; i++)
        {
            var fromChild = fromRoot.GetChild(i);

            var toChild = toRoot.Find(fromChild.name);
            if (toChild == null)
            {
                var go = new GameObject(fromChild.name);
                toChild = go.transform;
                toChild.parent = toRoot;
                toChild.localPosition = fromChild.localPosition;
                toChild.localEulerAngles = fromChild.localEulerAngles;
                toChild.localScale = fromChild.localScale;
            }
            else
            {
                if (toChild.localPosition != fromChild.localPosition ||
                    toChild.localEulerAngles != fromChild.localEulerAngles ||
                    toChild.localScale != fromChild.localScale)
                {
                    Debug.LogError("骨骼位置不统一：" + GetNodePath(fromChild), fromChild);
                }
            }

            MergeNodes(fromChild, toChild);
        }
    }

    private static Transform FindNode(Transform root, string path)
    {
        var node = root.Find(path);
        if (node == null)
        {
            Debug.LogError("骨骼没有找到：" + path);
        }

        return node;
    }

    private static string GetNodePath(Transform node)
    {
        string path = node.name;
        var parent = node.parent;
        while (parent.name != "Root")
        {
            path = parent.name + "/" + path;
            parent = parent.parent;
        }

        //path = "Root/" + path;
        return path;
    }

    #endregion
}
